package com.jplus.framework.db.dynamic;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.logging.Logger;

import javax.sql.DataSource;

/**
 * 动态DataSource 路由<br>
 * 外部调用请使用：DynamicDataSource
 * 
 * @author Yuanqy
 *
 */
public abstract class AbstractRoutingDataSource implements DataSource {
	private Map<String, DataSource> targetDataSources;
	private DataSource defaultTargetDataSource;
	private boolean lenientFallback = false;

	/**
	 * 设置 全部 数据源
	 * 
	 * @param targetDataSources
	 */
	public void setTargetDataSources(Map<String, DataSource> targetDataSources) {
		this.targetDataSources = targetDataSources;
	}

	/**
	 * 设置默认数据源
	 * 
	 * @param defaultTargetDataSource
	 */
	protected void setDefaultTargetDataSource(DataSource defaultTargetDataSource) {
		this.defaultTargetDataSource = defaultTargetDataSource;
	}

	/**
	 * 如果没有指定数据源，是否使用默认数据源
	 * 
	 * @param lenientFallback
	 */
	public void setLenientFallback(boolean lenientFallback) {
		this.lenientFallback = lenientFallback;
	}

	public void init() {
		if (this.targetDataSources == null) {
			throw new IllegalArgumentException("Property 'targetDataSources' isEmpty");
		}
		if (this.defaultTargetDataSource == null) {
			for (Map.Entry<String, DataSource> entry : this.targetDataSources.entrySet()) {
				if (entry.getValue() != null) {
					this.defaultTargetDataSource = entry.getValue();
					break;
				}
			}
		}
	}

	/**
	 * 获取动态数据源
	 * 
	 * @return
	 */

	private DataSource determineTargetDataSource() {
		String lookupKey = determineCurrentLookupKey();
		DataSource dataSource = (DataSource) this.targetDataSources.get(lookupKey);
		if ((dataSource == null) && ((this.lenientFallback) || (lookupKey == null))) {
			dataSource = this.defaultTargetDataSource;
		}
		if (dataSource == null) {
			throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
		}
		return dataSource;
	}

	/**
	 * 用户动态数据源 Key
	 * 
	 * @return
	 */
	protected abstract String determineCurrentLookupKey();

	@Override
	public Connection getConnection() throws SQLException {
		return determineTargetDataSource().getConnection();
	}

	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		return determineTargetDataSource().getConnection(username, password);
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		return (iface.isInstance(this)) || (determineTargetDataSource().isWrapperFor(iface));
	}

	@Override
	public int getLoginTimeout() throws SQLException {
		return 0;
	}

	@Override
	public void setLoginTimeout(int timeout) throws SQLException {
		throw new UnsupportedOperationException("setLoginTimeout");
	}

	@Override
	public PrintWriter getLogWriter() {
		throw new UnsupportedOperationException("getLogWriter");
	}

	@Override
	public void setLogWriter(PrintWriter pw) throws SQLException {
		throw new UnsupportedOperationException("setLogWriter");
	}

	@Override
	@SuppressWarnings("unchecked")
	public <T> T unwrap(Class<T> iface) throws SQLException {
		if (iface.isInstance(this)) {
			return (T) this;
		}
		throw new SQLException("DataSource of type [" + getClass().getName() + "] cannot be unwrapped as [" + iface.getName() + "]");
	}

	@Override
	public Logger getParentLogger() {
		return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
	}
}
